package com.wanban.download

import android.content.Intent
import android.text.TextUtils
import android.util.Log
import com.wanban.download.base.BaseDownloadUtils
import com.wanban.download.db.DatabaseManager
import com.wanban.download.db.DownloadEntity
import com.wanban.download.db.DownloadSQLiteHelper
import com.wanban.download.listener.OnFileDownloadListener
import com.wanban.download.listener.OnLoadCoverImageListener
import com.wanban.download.model.DownloadType
import com.wanban.download.model.Status
import com.wanban.download.service.DownloadService
import com.wanban.download.utils.DownloadConstants
import com.wanban.download.downloadutils.DownloadUtils
import com.wanban.download.downloadutils.Media3DownloadUtils
import com.wanban.download.utils.NetworkUtils
import com.wanban.download.utils.PathUtils
import com.wanban.download.utils.RxJavaUtils
import com.wanban.download.downloadutils.TXDownloadUtils
import com.wanban.download.utils.VideoDownloadSPUtil
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus

class DefaultDownloadManager{

    companion object {
        /**
         * 根据状态，判断当前任务是否处于下载状态
         */
        fun isTaskInRunningState(task: DownloadEntity): Boolean {
            return task.state == Status.PREPARE || task.state == Status.START || task.state == Status.DOWNLOADING
        }
    }

    // 下载器
    private val downloadUtils by lazy { HashMap<String, BaseDownloadUtils>() }
    // 任务列表, 只包含未完成的任务
    private val downloadQueue by lazy { ArrayList<DownloadEntity>() }
    // 图片缓存加载
    private var onLoadCoverImageListener: OnLoadCoverImageListener? = null

    /**
     * 设置当前的任务队列
     */
    internal fun setTasks(tasks: ArrayList<DownloadEntity>) {
        downloadQueue.clear()
        downloadQueue.addAll(tasks)
    }

    /**
     * 启动下载任务
     */
    @Synchronized
    internal fun resumeAllDownloads() {
        val currentRunningTaskCount = getRunningTaskCount()
        val canRunningNum = Params.MAX_DOWNLOAD_PARALLEL_NUMBER - currentRunningTaskCount
        if (canRunningNum < 0) return
        // 从任务队列中取出PENDING的任务，按创建时间排序，取canRunningNum个任务，用于启动任务
        downloadQueue.filter { it.state == Status.PENDING }.sortedBy { it.createTime }.take(canRunningNum).forEach { task ->
            if (TextUtils.isEmpty(canStartTask(task.url))) {
                Log.e("ExchangeManager", "resumeAllDownloads ：准备启动任务 -> ${task.downloadTag}....")
                // 更新状态
                onPrepareStatus(task)
                // 准备下载
                readyToDownload(task)
            } else {
                Log.e("ExchangeManager", "resumeAllDownloads ：无法启动 -> ${task.downloadTag}....")
            }
        }
    }
    /**
     * 启动下载任务
     */
    @Synchronized
    internal fun startDownload(task: DownloadEntity) {
        canAddTask(task.url) { canAddTask, _ ->
            if (!canAddTask) { // 如果不允许下载的
                return@canAddTask
            }
            // 加入下载队列的时间
            task.createTime = System.currentTimeMillis()
            // 赋予下载根目录，之后的下载文件都是依据此目录来配置
            RxJavaUtils.doAsync({
                task.fileRootPath = PathUtils.getRootPath(task.downloadTag)
                return@doAsync task.fileRootPath
            },{ path ->
                // 如果文件目录无法正常创建的话，直接提示下载失败
                if (TextUtils.isEmpty(path)) {
                    onErrorStatus(task, "无法创建下载文件！")
                    return@doAsync
                }
                // 先处于准备阶段，到开始下载之间的过程都是准备阶段
                onPendingStatus(task, false)
                // 加入下载队列
                downloadQueue.add(task)
                // 判断是否能启动当前任务
                val runningCount = getRunningTaskCount()
                if (runningCount >= Params.MAX_DOWNLOAD_PARALLEL_NUMBER) { // 已达到最大并行任务个数
                    onPendingStatus(task, true)
                    return@doAsync
                }
                onPrepareStatus(task)
                // 准备下载
                readyToDownload(task)
            },{
                onErrorStatus(task, "无法创建下载文件！")
            })
        }
    }


    /**
     * 停止当前任务
     */
    @Synchronized
    internal fun stopDownload(url: String){
        // 暂停下载
        stopDownloadUtils(url)
        // 继续下一个
        getTaskByUrl(url) { task ->
            if (task == null) {
                // 准备启动下一个
                hasNextTaskToStart()
                return@getTaskByUrl
            }
            onStopStatus(task)
            // 准备启动下一个
            hasNextTaskToStart()
        }
    }

    /**
     * 恢复当前任务
     */
    @Synchronized
    internal fun resumeDownload(url: String){
        getTaskByUrl(url) { result ->
            if (result == null) return@getTaskByUrl
            if (!TextUtils.isEmpty(canStartTask(url))) { // 不允许启动任务
                return@getTaskByUrl
            }
            // 启动任务有2种情况，1：直接启动当前任务，2：要停止一个执行最长时间的任务，再启动当前任务
            val runningTaskCount = getRunningTaskCount()
            if (runningTaskCount < Params.MAX_DOWNLOAD_PARALLEL_NUMBER) { // 直接启动当前任务即可
                onPrepareStatus(result)
                // 准备下载
                readyToDownload(result)
                return@getTaskByUrl
            }
            // 找到运行时间最长的任务，stop
            getRunningTasks().maxByOrNull { it.startTaskTime }?.let { oldTask ->
                // 只暂停当前正在下载的任务，不发送任务状态
                stopDownloadUtils(oldTask.url)
                // 更新旧任务状态
                onPendingStatus(oldTask, true)
                // 启动新任务
                onPrepareStatus(result)
                // 准备下载
                readyToDownload(result)
            }
        }
    }
    /**
     * 删除当前任务
     */
    @Synchronized
    internal fun deleteDownload(url: String){
        getTaskByUrl(url) { task ->
            if (task == null) {
                hasNextTaskToStart()
                return@getTaskByUrl
            }
            execDelete(task)
            // 回调
            onDeleteStatus(task)
            // 准备启动下一个
            hasNextTaskToStart()
        }
    }

    private fun execDelete(task: DownloadEntity){
        // 停止解析或者下载，并删除文件
        if (downloadUtils.containsKey(task.url)) { // 正在下载中的视频删除
            // 找到指定的下载器
            val utils = downloadUtils.remove(task.url)
            // 删除下载
            utils?.deleteDownloadFile(task)
            if (task.downloadType != DownloadType.DEFAULT) { // tx和media3都有文件需要删除
                PathUtils.deleteTaskFiles(task)
            }
        } else { // 已完成的视频
            // 删除文件
            if (task.downloadType == DownloadType.TX) { // 删除tx下载的已完成视频
                TXDownloadUtils.deleteTask(task.url)
            } else if (task.downloadType == DownloadType.MEDIA3) { // 删除media3下载的已完成视频
                Media3DownloadUtils.deleteTask(task.downloadTag)
            }
            // 默认下载方式删除以及删除部分通用文件
            PathUtils.deleteTaskFiles(task)
        }

        // 清数据
        downloadQueue.remove(task)
        // 删表格
        DatabaseManager.getInstance()?.delete(task.url)
    }

    /**
     * 删除指定任务队列
     */
    @Synchronized
    internal fun deleteDownloads(deleteTasks: List<DownloadEntity>, complete: () -> Unit){
        RxJavaUtils.doAsync({
            for (task in deleteTasks) {
                val delete = getTaskByUrlSync(task.url)?:continue
                // 停止解析或者下载，并删除文件
                execDelete(delete)
                // 间隔部分时间，避免删除文件指令失效
                Thread.sleep(100)
            }
            return@doAsync true
        },{
            deleteTasks.forEach {
                it.state = Status.DELETE
                sendTaskEvent(it)
            }
            complete()
            hasNextTaskToStart()
        },{
            complete()
            hasNextTaskToStart()
        })
    }

    internal fun clear(){
        downloadUtils.keys.forEach {
            val until = downloadUtils[it]
            until?.stopDownload()
        }
        downloadUtils.clear()
        downloadQueue.forEach {
            it.state = Status.DELETE
            sendTaskEvent(it)
        }
        downloadQueue.clear()
    }

    /**
     * 返回可以重新运行的任务，即除了IDLE、STOP、ERROR、SUCCESS 和 DELETE 状态外的所有任务
     */
    internal fun getCanRunningTasks(): ArrayList<DownloadEntity>{
        return downloadQueue.filter {
            it.state == Status.PENDING || it.state == Status.PREPARE || it.state == Status.START || it.state == Status.DOWNLOADING
        } as ArrayList<DownloadEntity>
    }

    /**
     * 返回所有已完成的任务
     */
    internal fun getSuccessTasks(complete: (tasks:ArrayList<DownloadEntity>) -> Unit){
        RxJavaUtils.doAsync({
            val selection = DownloadSQLiteHelper.Columns.STATE + "=?"
            return@doAsync DatabaseManager.getInstance()?.getTasks(selection, arrayOf(Status.SUCCESS.toString()))
        },{
            complete(it)
        },{
            complete(ArrayList())
        })
    }



    /**
     * 返回所有已完成的任务
     */
    internal fun getUnSuccessTasks(): ArrayList<DownloadEntity>{
        return downloadQueue.filter {
            it.state != Status.IDLE && it.state != Status.SUCCESS && it.state != Status.DELETE
        } as ArrayList<DownloadEntity>
    }


    /**
     * 各种状态
     */
    private fun onPendingStatus(task: DownloadEntity, isUpdateTask: Boolean) {
        Log.d(WBDownloadUtils.TAG, "${task.downloadTag} -> onPending...")
        task.state = Status.PENDING
        if (isUpdateTask) {
            updateTaskDatabase(task)
        }
        sendTaskEvent(task)
    }
    private fun onPrepareStatus(task: DownloadEntity) {
        Log.d(WBDownloadUtils.TAG, "${task.downloadTag} -> onPrepare...")
        task.state = Status.PREPARE
        updateTaskDatabase(task)
        sendTaskEvent(task)
    }
    private fun onStartStatus(task: DownloadEntity) {
        Log.d(WBDownloadUtils.TAG, "${task.downloadTag} -> onStart...")
        task.state = Status.START
        updateTaskDatabase(task)
        sendTaskEvent(task)
    }
    private fun onStopStatus(task: DownloadEntity) {
        task.state = Status.STOP
        updateTaskDatabase(task)
        sendTaskEvent(task)
    }
    private fun onDownloadingStatus(task: DownloadEntity) {
        task.state = Status.DOWNLOADING
        updateTaskDatabase(task)
        sendTaskEvent(task)
    }
    private fun onErrorStatus(task: DownloadEntity, errorMsg: String) {
        Log.d(WBDownloadUtils.TAG, "${task.downloadTag} -> onError...$errorMsg")
        // 停止下载
        stopDownloadUtils(task.url)
        // 更新状态，并发送消息
        if (!NetworkUtils.isConnected()) {
            task.state = Status.PENDING
            task.isNetworkError = true
            // 更新并发送消息
            updateTaskDatabase(task)
            sendTaskEvent(task)
            return
        }
        task.errorMsg = errorMsg
        task.state = Status.ERROR
        updateTaskDatabase(task)
        sendTaskEvent(task)

        // 准备启动下一个
        hasNextTaskToStart()
    }

    private fun onSuccessStatus(task: DownloadEntity) {
        Log.d(WBDownloadUtils.TAG, "${task.downloadTag} -> onComplete..." + task.playPath)
        task.state = Status.SUCCESS
        task.percent = 100f
        // 清数据
        downloadQueue.remove(task)
        // 准备启动下一个
        hasNextTaskToStart()
        updateTaskDatabase(task)
        sendTaskEvent(task)
    }

    private fun onDeleteStatus(task: DownloadEntity) {
        Log.e(WBDownloadUtils.TAG, "${task.downloadTag} -> onDeleteStatus..." + task.url)
        task.state = Status.DELETE
        task.m3u8?.tsList?.clear()
        task.m3u8 = null
        sendTaskEvent(task)
    }

    // 更新数据库
    private fun updateTaskDatabase(task: DownloadEntity){
        // 只有解析成功的时候，才会更新m3u8
        RxJavaUtils.doAsync {
            return@doAsync DatabaseManager.getInstance()?.insert(task, false)
        }
    }


    /**
     * 发送任务event
     */
    private fun sendTaskEvent(task: DownloadEntity){
        if (EventBus.getDefault().hasSubscriberForEvent(DownloadEntity::class.java)) {
            EventBus.getDefault().post(task)
        }
    }

    /**
     * 通过url获取任务
     */
    fun getTaskByUrlSync(url: String?): DownloadEntity? {
        // 根据url获取task
        if (url == null) return null
        val task = downloadQueue.firstOrNull { it.url == url }
        if (task != null) return task
        val selection = DownloadSQLiteHelper.Columns.URL + "=?"
        return DatabaseManager.getInstance()?.getTask(selection, arrayOf(url))
    }

    internal fun canAddTask(url: String, complete: (canAddTask: Boolean, errorMsg: String?) -> Unit){
        val errorMsg = canStartTask(url)
        if (!TextUtils.isEmpty(errorMsg)) {
            complete(false, errorMsg)
            return
        }
        // 判断任务栈是否已达到上线
        if (getDownloadQueueCount() >= Params.DEFAULT_DOWNLOAD_QUEUE_NUMBER) {
            complete(false, DownloadConstants.ERROR_DOWNLOAD_MAX_QUEUE)
            return
        }

        // 先查是否在下载队列中
        if (isAddedInQueue(url)) {
            complete(false, DownloadConstants.ERROR_DOWNLOAD_IN_QUEUE)
            return
        }
        Observable.create {
            val selection = DownloadSQLiteHelper.Columns.URL + "=?"
            val result = DatabaseManager.getInstance()?.getTask(selection, arrayOf(url))
            val isAddInSql = result != null
            if (isAddInSql) {
                result?.errorMsg = DownloadConstants.ERROR_DOWNLOAD_ADDED
            }
            it.onNext(!isAddInSql)
        }.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({
                val errMsg = if (it) {
                    null
                } else {
                    DownloadConstants.ERROR_DOWNLOAD_IN_QUEUE
                }
                complete(it?:false, errMsg)
            }, {
                complete(false, null)
            }).isDisposed
    }

    private fun cacheCoverImageFile(task: DownloadEntity) {
        val coverFile = PathUtils.getCoverFile(task.fileRootPath)?:return
        if (coverFile.exists() && coverFile.length() > 0) {
            return
        }
        RxJavaUtils.doAsync {
            onLoadCoverImageListener?.onLoadCoverImageAsync(task.coverUrl, coverFile)
            return@doAsync true
        }
    }

    private fun getTaskByUrl(url: String, complete: (task: DownloadEntity?) -> Unit){
        // 根据url获取task
        val task = downloadQueue.firstOrNull { it.url == url }
        if (task == null) { // 当前下载队列没有内容，就尝试从数据库获取
            RxJavaUtils.doAsync({
                val selection = DownloadSQLiteHelper.Columns.URL + "=?"
                return@doAsync DatabaseManager.getInstance()?.getTask(selection, arrayOf(url))
            },{
                complete(it)
            },{
                complete(null)
            })
        } else {
            complete(task)
        }
    }


    /**
     * 返回正在下载的任务数量
     */
    private fun getRunningTasks(): ArrayList<DownloadEntity> {
        return downloadQueue.filter {
            isTaskInRunningState(it)
        } as ArrayList
    }

    /**
     *  返回正在运行的任务栈数量
     */
    private fun getRunningTaskCount(): Int {
        return getRunningTasks().size
    }


    /**
     * 判断当前任务是否能启动
     *  主要检测 URL，网络状态，是否允许移动流量下载
     */
    private fun canStartTask(url: String): String? {
        if (TextUtils.isEmpty(url)) {
            return DownloadConstants.ERROR_URL_NULL
        }
        // 判断网络是允许下载
        // 没有网络
        if (!NetworkUtils.isConnected()) {
            return DownloadConstants.ERROR_NETWORK_NO_CONNECT
        }
        // 不允许使用流量下载
        if (NetworkUtils.isMobileData() && WBDownloadUtils.isDownloadOnlyWifi()) {
            return DownloadConstants.ERROR_ONLY_WIFI
        }
        return null
    }

    /**
     * 当网络状态恢复的时候，启动之前任务
     */
    internal fun resumeFromNetworkConnected(){
        downloadQueue.filter { it.isNetworkError }.forEach { task ->
            task.isNetworkError = false
            onPrepareStatus(task)
            // 准备下载
            readyToDownload(task)
        }
    }

    // 判断当前任务是否在下载队列中
    private fun isAddedInQueue(url: String): Boolean {
        return downloadQueue.any { it.url == url }
    }


    // 返回当前任务栈的数量，不包括IDLE，DELETE，SUCCESS 和 ERROR
    private fun getDownloadQueueCount(): Int {
        return downloadQueue.count {
            it.state == Status.PENDING || it.state == Status.PREPARE
                    || it.state == Status.START || it.state == Status.DOWNLOADING
        }
    }

    /**
     * 检查是否还有后续任务
     */
    private fun hasNextTaskToStart(){
        // 如果当前运行的任务，达到并行最大值，不在启动新任务
        if (getRunningTaskCount() >= Params.MAX_DOWNLOAD_PARALLEL_NUMBER) return
        // 查询处于排队的任务，按create进行排序，找到最久启动的任务
        val nextTask = downloadQueue.filter { it.state == Status.PENDING }.minByOrNull { it.createTime }
        if (nextTask == null) {// 已经没有任何下载
            if (downloadQueue.isEmpty()) {
                Log.e(WBDownloadUtils.TAG, "所有任务都已经完成...")
                ContextProvider.application.stopService(Intent(ContextProvider.application, DownloadService::class.java))
            } else {
                Log.e(WBDownloadUtils.TAG, "没有可继续执行的任务...")
            }
            return
        }
        // 判断任务是否能执行
        if (!TextUtils.isEmpty(canStartTask(nextTask.url))) return
        // 更新状态
        onPrepareStatus(nextTask)
        // 准备下载
        readyToDownload(nextTask)
    }


    fun stopDownloadUtils(url: String?){
        if (downloadUtils.containsKey(url)) {
            val utils = downloadUtils.remove(url)
            utils?.stopDownload()
        }
    }
    /**
     * 准备下载下一个
     */
    private fun readyToDownload(task: DownloadEntity){
        if (TextUtils.isEmpty(task.fileRootPath)) {
            onErrorStatus(task, "无法创建下载文件！")
            return
        }

        if (!TextUtils.isEmpty(task.coverUrl)) { // 缓存图片
            cacheCoverImageFile(task)
        }
        // 先暂停下载
        stopDownloadUtils(task.url)

        val utils = when (task.downloadType) {
            DownloadType.DEFAULT -> {
                DownloadUtils()
            }
            DownloadType.TX -> {
                TXDownloadUtils()
            }
            else -> {
                Media3DownloadUtils()
            }
        }
        utils.startDownload(task, object: OnFileDownloadListener {
            override fun onStart(url: String) {
                val currentTask = getTaskByUrlSync(url)?:return
                onStartStatus(currentTask)
            }

            override fun onProgress(url: String, speed: Long, currentLength: Long, percent: Float) {
                val currentTask = getTaskByUrlSync(url)?:return
                currentTask.speed = speed
                currentTask.percent = percent
                currentTask.contentLength = currentLength
                onDownloadingStatus(currentTask)
            }

            override fun onError(url: String, errorMsg: String) {
                val currentTask = getTaskByUrlSync(url)?:return
                onErrorStatus(currentTask, errorMsg)
            }

            override fun onComplete(url: String, length: Long, path: String?) {
                val currentTask = getTaskByUrlSync(url)?:return
                currentTask.playPath = path
                onSuccessStatus(currentTask)
//                if (task.downloadType == DownloadType.DEFAULT) {
//                    Log.e("Merge", "准备合并文件....")
//                    MergeTsUtils.transformM3U8ToMp4(currentTask.downloadTag, { mp4Path ->
//                        currentTask.playPath = mp4Path
//                        onSuccessStatus(currentTask)
//                    } ,{
//                        currentTask.playPath = path
//                        onSuccessStatus(currentTask)
//                    })
//                } else {
//                    onSuccessStatus(currentTask)
//                }
            }
        })
        downloadUtils[task.url] = utils
    }

    /**
     * 设置图片下载监听
     */
    fun setOnLoadCoverImageListener(onLoadCoverImageListener: OnLoadCoverImageListener?){
        this.onLoadCoverImageListener = onLoadCoverImageListener
    }

    fun getDownloadType(): Int{
        return VideoDownloadSPUtil.instance.currentDownloadType
    }

    fun setDownloadType(@DownloadType type: Int){
        VideoDownloadSPUtil.instance.currentDownloadType = type
    }
}